using System.Linq;
using System.Web.Mvc;
using ExtendedMvc.Common.Diagnostics;
using ExtendedMvc.Html;

namespace ExtendedMvc.Elements {
	public abstract class FormElement<T, TModel> : VisibleElement<T>
		where T : FormElement<T, TModel>
		where TModel : class {
		private bool _required;
		private bool _disabled;
		private string _onChange;
		private ValidationMessage<TModel> _validationMessage;
		private Label<TModel> _label;
		private LabelPosition _labelPosition;

		//protected ValidationMessage<TModel> validationMessage { get { return _validationMessage; } }

		//protected Label<TModel> label { get { return _label; } }

		protected IModelViewHelper<TModel> helper { get; private set; }

		protected string memberName { get; private set; }

		protected ModelMetadata metadata { get; private set; }

		protected FormElement(IModelViewHelper<TModel> html, string tag, string memberName, string id = null)
			: base(tag, id ?? memberName) {
			this.helper = html;
			Check.Argument.IsNotNullOrWhiteSpace(memberName, "memberName");
			this.memberName = memberName;
			metadata = html.GetMetadataForProperty(memberName);
			if (metadata != null) _required = metadata.IsRequired;
		}

		protected virtual bool HasError {
			get {
				if (!helper.ViewContext.ViewData.ModelState.ContainsKey(memberName)) return false;
				var modelState = helper.ViewContext.ViewData.ModelState[memberName];
				return (modelState != null && modelState.Errors != null && modelState.Errors.Any());
			}
		}

		protected virtual string ErrorMessage {
			get {
				if (!HasError) return null;
				return helper.ViewContext.ViewData.ModelState[memberName].Errors[0].ErrorMessage;
			}
		}

		public virtual T Disabled(bool value = true) {
			_disabled = value;
			return (T)this;
		}

		public virtual T Required(bool value = true) {
			_required = value;
			return (T)this;
		}

		#region Events
		public virtual T OnChange(string value) {
			_onChange = value;
			return (T)this;
		}
		#endregion

		protected virtual bool UseNameAttribute { get { return true; } }

		protected override void SetId() {
			if (UseNameAttribute && !string.IsNullOrWhiteSpace(memberName)) this.SetAttribute(HtmlAttribute.Name, memberName);
			base.SetId();
		}

		protected override void Prepare() {
			if (!string.IsNullOrWhiteSpace(_onChange)) SetAttribute(HtmlEventAttribute.OnChange, _onChange);
			if (_required) SetAttribute(HtmlAttribute.Required, HtmlAttribute.Required);
			if (_disabled) SetAttribute(HtmlAttribute.Disabled, HtmlAttribute.Disabled);
			if (_label != null) {
				if (_labelPosition == LabelPosition.After) AddAfter(_label.ToString());
				else AddBefore(_label.ToString());
			}
			if (HasError) CssClass("input-validation-error");
			if (_validationMessage != null) AddAfter(_validationMessage.ToString());
			base.Prepare();
		}

		protected virtual LabelPosition defaultLabelPosition { get { return LabelPosition.Before; } }

		public virtual T AddLabel(string text = null, LabelPosition? position = null, string labelFormat = null, string cssClass = null) {
			_labelPosition = position ?? defaultLabelPosition;
			labelFormat = labelFormat ?? (_labelPosition == LabelPosition.After ? "&nbsp;{0}" : "{0}:&nbsp;");
			_label = new Label<TModel>(helper, memberName, id).Text(text).Format(labelFormat).CssClass(cssClass);
			return (T)this;
		}

		public virtual T AddValidationMessage(string message = null, string messageFormat = null) {
			var errorMessage = message ?? ErrorMessage;
			if (!string.IsNullOrEmpty(errorMessage)) _validationMessage = new ValidationMessage<TModel>(helper, memberName).DefaultMessage(string.Format(messageFormat ?? "{0}", errorMessage));
			return (T)this;
		}
	}
}
